package com.egg.eggdemo;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.concurrent.TimeUnit;

// JedisPool 是连接 Redis 的线程安全连接池，避免每次都创建新连接
public class RedisLockUtil {
    private final JedisPool jedisPool;

    public RedisLockUtil(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 尝试获取分布式锁(tryLock 方法)
     * @param lockKey 锁的key
     * @param requestId 请求标识(用于释放锁时校验,防止误解锁)
     * @param expireTime 过期时间(秒),防止死锁
     * @param waitTime 最大等待时间(毫秒)，超时后放弃获取锁
     * @return 是否获取锁成功
     */
    public boolean tryLock(String lockKey, String requestId, int expireTime, long waitTime) {
        try (Jedis jedis = jedisPool.getResource()) {
            long end = System.currentTimeMillis() + waitTime;  //系统时间+最大等待时间

            while (System.currentTimeMillis() < end) {  //没有超时的情况下
//                String result = jedis.set(lockKey,requestId, "NX", "EX", String.valueOf(expireTime));

                //- `nx()`：表示"if not exists"，即当key不存在时，我们进行set操作；若key已经存在，则不做任何操作
                //- `ex(expireTime)`：设置过期时间，防止死锁，即要给这个key加一个过期的设置
                //- `requestId`：用于标识锁的持有者，确保只有持有者才能释放锁
                SetParams params = SetParams.setParams().nx().ex(expireTime);
                String result = jedis.set(lockKey, requestId, params);  // 通过给value赋值为requestId，我们就知道这把锁是哪个请求加的了
                if ("OK".equals(result)) {
                    return true;
                }
                try {
                    Thread.sleep(100); // 短暂等待后重试
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        return false;  // 超时未获取
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁的key
     * @param requestId 请求标识
     */
    public void releaseLock(String lockKey, String requestId) {
        try (Jedis jedis = jedisPool.getResource()) {
            // 使用Lua脚本保证原子性
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "return redis.call('del', KEYS[1]) " +
                    "else return 0 end";
            jedis.eval(script, 1, lockKey, requestId);
        }
    }
}
